/************************************************************
* @file   gkt_clkrst.h
* @brief  peripheral clock & reset
************************************************************/

#ifndef GKT_CLKRST_H
#define GKT_CLKRST_H

#include "gkt_types.h"
#include "gkt_defines.h"

#ifdef __cplusplus
extern "C" {
#endif

#define GKT_CLKRST_FIELD_L1(name, field)	\
	GKT_CLKRST_##name##_##field
#define GKT_CLKRST_FIELD(name, field)	\
	GKT_CLKRST_FIELD_L1(name, field)

#define GKT_CLKRST_ITEM_L1(name, field, item)	\
	GKT_CLKRST_##name##_##field##_##item
#define GKT_CLKRST_ITEM(name, field, item)	\
	GKT_CLKRST_ITEM_L1(name, field, item)

#define GKT_CLKRST_CLOCK(name)	\
	GKT_CLKRST_FIELD(name, CLOCK)
#define GKT_CLKRST_CLOCK_MAGIC(name)	\
	GKT_CLKRST_FIELD(name, CLOCK_MAGIC)
#define GKT_CLKRST_CLOCK_ID(name)	\
	GKT_CLKRST_FIELD(name, CLOCK_ID)
#define GKT_CLKRST_CLOCK_DIV(name)	\
	GKT_CLKRST_FIELD(name, CLOCK_DIV)
#define GKT_CLKRST_CLOCK_MAX(name)	\
	GKT_CLKRST_FIELD(name, CLOCK_MAX)

#define GKT_CLKRST_SHIFT(name, field)	\
	GKT_CLKRST_ITEM(name, field, SHIFT)
#define GKT_CLKRST_BITS(name, field)	\
	GKT_CLKRST_ITEM(name, field, BITS)
#define GKT_CLKRST_MASKS(name, field)	\
	GKT_CLKRST_ITEM(name, field, MASKS)

#include "gkt_clkrst_defines.h"
#include "gkt_clkrst_default_config.h"

#ifndef GKT_CLKRST_SHIFT_MAX
#define GKT_CLKRST_SHIFT_MAX	GKT_INTBITS
#endif


/* clock id enum */
#undef GKT_CLKRST_CLOCK_ITEM
#define GKT_CLKRST_CLOCK_ITEM(name)	\
	GKT_CLKRST_##name##_CLOCK_ID
enum {
	GKT_CLKRST_CLOCK_ITEMS,
	GKT_CLKRST_CLOCK_ID_MAX
};


/*
 * @brief get source clock
 * @example
 *   GKT_CLKRST_SRC_CLOCK(PER48M)
 * @pre-defined macros
 *   #define GKT_CLKRST_SYSTEM_CLOCK	0xXXXXXXXX
 *   #define GKT_CLKRST_PER48M_CLOCK_SRC	SYSTEM
 */
#define GKT_CLKRST_SRC_CLOCK_L2(src_name)	\
	GKT_CLKRST_CLOCK(src_name)
#define GKT_CLKRST_SRC_CLOCK_L1(src_name)	\
	GKT_CLKRST_SRC_CLOCK_L2(src_name)
#define GKT_CLKRST_SRC_CLOCK(name)	\
	GKT_CLKRST_SRC_CLOCK_L1(GKT_CLKRST_##name##_CLOCK_SRC)

/* 
 * @brief get source clock id
 * @example
 *   GKT_CLKRST_SRC_CLOCK_ID(PER48M)
 * @pre-defined macros / enumerations
 *   #define GKT_CLKRST_SYSTEM_CLOCK_ID		6
 *   #define GKT_CLKRST_PER48M_CLOCK_SRC	SYSTEM
 */
#define GKT_CLKRST_SRC_CLOCK_ID_L2(src_name)	\
	GKT_CLKRST_CLOCK_ID(src_name)
#define GKT_CLKRST_SRC_CLOCK_ID_L1(src_name)	\
	GKT_CLKRST_SRC_CLOCK_ID_L2(src_name)
#define GKT_CLKRST_SRC_CLOCK_ID(name)	\
	GKT_CLKRST_SRC_CLOCK_ID_L1(GKT_CLKRST_##name##_CLOCK_SRC)


/* 
 * region name 
 */
#define GKT_CLKRST_REGION_NAME(name)	GKT_CLKRST_##name##_REGION

#if defined(GKT_CLKRST_REGION_NUMS) && (GKT_CLKRST_REGION_NUMS > 1)
/*
 * region id (0, 1, ...)
 */
#define GKT_CLKRST_REGION_ID_L2(region_name)	\
	GKT_CLKRST_FIELD(region_name, REGION_ID)
#define GKT_CLKRST_REGION_ID_L1(region_name)	\
	GKT_CLKRST_REGION_ID_L2(region_name)
#define GKT_CLKRST_REGION_ID(name)	\
	GKT_CLKRST_REGION_ID_L1(GKT_CLKRST_REGION_NAME(name))

/* 
 * @brief get clock region baseaddr
 * @example
 *   GKT_CLKRST_REGION_BASEADDR(SYSTEM)
 * @pre-defined macros
 *   #define GKT_CLKRST_NAO_BASEADDR	0x400C0000
 *   #define GKT_CLKRST_SYSCLK_REGION	NAO
 */
#define GKT_CLKRST_REGION_BASEADDR_L2(region_name)	\
	GKT_CLKRST_FIELD(region_name, REGION_BASEADDR)
#define GKT_CLKRST_REGION_BASEADDR_L1(region_name)	\
	GKT_CLKRST_REGION_BASEADDR_L2(region_name)
#define GKT_CLKRST_REGION_BASEADDR(name)	\
	GKT_CLKRST_REGION_BASEADDR_L1(GKT_CLKRST_REGION_NAME(name))
#else
/* region id: 0 */
#define GKT_CLKRST_REGION_ID(name)	0
#define GKT_CLKRST_REGION_BASEADDR(name)	GKT_CLKRST_BASEADDR
#endif


/* 
 * @brief get clock register offset in region
 * @example
 *   GKT_CLKRST_OFFSET(SYSTEM)
 * @pre-defined macros
 *   #define GKT_CLKRST_SYSTEM_OFFSET	0x0C
 */
#define GKT_CLKRST_OFFSET(name)	GKT_CLKRST_FIELD(name, OFFSET)

/* 
 * @brief get clock register address
 */
#define GKT_CLKRST_ADDRESS(name)	\
	(GKT_CLKRST_REGION_BASEADDR(name) + GKT_CLKRST_OFFSET(name))


/* clkrst desc type */
enum {
	GKT_CLKRST_DESC_BASE = 0,
	GKT_CLKRST_DESC_FDIV,
	GKT_CLKRST_DESC_CDIV,
	GKT_CLKRST_DESC_MSCLK,
	GKT_CLKRST_DESC_TYPE_MAX
};
#define GKT_CLKRST_DESC_TYPE_MASK	0x0F
#define GKT_CLKRST_DESC_ET_DBCLK	0x10

/*
 * common/base descriptor
 *   1. clock divider is fixed;
 *   2. the peripheral driver does not need to know the clock value;
 */
typedef struct _gkt_clkrst_common_desc {
	uint8_t	type;
	uint8_t	region_id;
	gkt_clkrst_offset_t	offset;
	uint8_t	clken_shift;	/* shift of clock_en */
	uint8_t	rstn_shift;		/* shift of reset_n */
	uint8_t	src_clkid;
	uint8_t	clkid;
} gkt_clkrst_common_desc_s, gkt_clkrst_base_desc_s;

#define GKT_CLKRST_COMMON_DESC(name, CLOCK_TYPE, CLOCK_ET, clock_id)	\
	{	\
		.type = GKT_CLKRST_DESC_##CLOCK_TYPE | CLOCK_ET,	\
		.region_id	= GKT_CLKRST_REGION_ID(name),	\
		.offset = GKT_CLKRST_OFFSET(name),	\
		.clken_shift = GKT_CLKRST_SHIFT(name, CLOCK_EN),	\
		.rstn_shift = GKT_CLKRST_SHIFT(name, RESET_N),	\
		.src_clkid	= GKT_CLKRST_SRC_CLOCK_ID(name),	\
		.clkid	= clock_id,	\
	}

#define GKT_CLKRST_BASE_DESC(name, clock_id)	\
	{ .base = GKT_CLKRST_COMMON_DESC(name, BASE, 0, clock_id) }

/*
 * base & debounce clock descriptor
 */
typedef struct _gkt_clkrst_base_dbclk_desc {
	gkt_clkrst_common_desc_s	common;
	uint8_t	dbclk_shift;
	uint8_t	dbclk_bits;
} gkt_clkrst_base_dbclk_desc_s;

#define GKT_CLKRST_BASE_DBCLK_DESC(name, clock_id)	\
	{	\
		.base_dbclk = {	\
			.common	= GKT_CLKRST_COMMON_DESC(name, BASE, GKT_CLKRST_DESC_ET_DBCLK, clock_id),	\
			.dbclk_shift	= GKT_CLKRST_SHIFT(name, DBCLK),	\
			.dbclk_bits	= GKT_CLKRST_BITS(name, DBCLK),	\
		}	\
	}

/*
 * fixed divider descriptor
 *   1. clock divider is fixed;
 *   2. the peripheral driver could use the clock value to do other configurations;
 */
typedef struct _gkt_clkrst_fdiv_desc {
	gkt_clkrst_common_desc_s	common;
	uint16_t	divider;
} gkt_clkrst_fdiv_desc_s;
#define GKT_CLKRST_FDIV_DESC(name, clock_id)	\
	{	\
		.fdiv = {	\
			.common	= GKT_CLKRST_COMMON_DESC(name, FDIV, 0, clock_id),	\
			.divider	= GKT_CLKRST_CLOCK_DIV(name),\
		}	\
	}


/* Clock Divider Type */
enum {
	GKT_CLKRST_CLKDIV_MASKS = 0,
	GKT_CLKRST_CLKDIV_EVEN,
	GKT_CLKRST_CLKDIV_MAX
};
#define GKT_CLKRST_CLKDIV_MASKS_MAX_DIV	31

typedef struct _gkt_clkrst_cdiv_attr {
	uint8_t	clkdiv_type;	/* MASKS/EVEN ... */
	uint8_t	clkdiv_shift;
	uint8_t	clkdiv_bits;
	union {
		uint32_t	masks;
		struct {
			uint16_t	min;
			uint16_t	max;
		} range;
	} clkdiv;
	uint32_t	clock_max;	/* clock max */
} gkt_clkrst_cdiv_attr_s;

#define GKT_CLKRST_CDIV_MASKS_ATTR(name)	\
	{	\
		.clkdiv_type	= GKT_CLKRST_CLKDIV_MASKS,	\
		.clkdiv_shift	= GKT_CLKRST_SHIFT(name, CLOCK_DIV),	\
		.clkdiv_bits	= GKT_CLKRST_BITS(name, CLOCK_DIV),	\
		.clkdiv.masks	= GKT_CLKRST_ITEM(name, CLOCK_DIV, MASKS),	\
		.clock_max	= GKT_CLKRST_CLOCK_MAX(name),	\
	}
#define GKT_CLKRST_CDIV_EVEN_ATTR(name)	\
	{	\
		.clkdiv_type	= GKT_CLKRST_CLKDIV_EVEN,	\
		.clkdiv_shift	= GKT_CLKRST_SHIFT(name, CLOCK_DIV),	\
		.clkdiv_bits	= GKT_CLKRST_BITS(name, CLOCK_DIV),	\
		.clkdiv.range.min	= GKT_CLKRST_ITEM(name, CLOCK_DIV, MIN),	\
		.clkdiv.range.max	= GKT_CLKRST_ITEM(name, CLOCK_DIV, MAX),	\
		.clock_max	= GKT_CLKRST_CLOCK_MAX(name),	\
	}
#define GKT_CLKRST_CDIV_ATTR_L2(name, CLKDIV_TYPE)	\
	GKT_CLKRST_CDIV_##CLKDIV_TYPE##_ATTR(name)
#define GKT_CLKRST_CDIV_ATTR_L1(name, CLKDIV_TYPE)	\
	GKT_CLKRST_CDIV_ATTR_L2(name, CLKDIV_TYPE)
#define GKT_CLKRST_CDIV_ATTR(name)	\
	GKT_CLKRST_CDIV_ATTR_L1(name, GKT_CLKRST_##name##_CLOCK_DIV_TYPE)

/*
 * configurable divider descriptor
 *   1. clock divider is configurable;
 *   2. the peripheral driver could use the clock value to do other configurations;
 */
typedef struct _gkt_clkrst_cdiv_desc {
	gkt_clkrst_common_desc_s	common;
	gkt_clkrst_cdiv_attr_s	attr;
} gkt_clkrst_cdiv_desc_s;
#define GKT_CLKRST_CDIV_DESC(name, clock_id)	\
	{	\
		.cdiv = {	\
			.common	= GKT_CLKRST_COMMON_DESC(name, CDIV, 0, clock_id),	\
			.attr	= GKT_CLKRST_CDIV_ATTR(name),	\
		}	\
	}

/*
 * mixed clkrst desc type for BASE/FDIV/CDIV descs
 */
typedef union _gkt_clkrst_desc {
	gkt_clkrst_base_desc_s	base;
	gkt_clkrst_base_dbclk_desc_s	base_dbclk;
	gkt_clkrst_fdiv_desc_s	fdiv;
	gkt_clkrst_cdiv_desc_s	cdiv;
} gkt_clkrst_desc_u;

#define GKT_CLKRST_DESC_TYPE_L1(name, TYPE, clock_id)	\
	GKT_CLKRST_##TYPE##_DESC(name, clock_id)
#define GKT_CLKRST_DESC_TYPE(name, TYPE, clock_id)	\
	GKT_CLKRST_DESC_TYPE_L1(name, TYPE, clock_id)

#define GKT_CLKRST_DESC(name)	\
	GKT_CLKRST_DESC_TYPE(name, GKT_CLKRST_##name##_CLOCK_TYPE, ~0)
#define GKT_CLKRST_DESC_WITH_CLOCK_ID(name)	\
	GKT_CLKRST_DESC_TYPE(name, GKT_CLKRST_##name##_CLOCK_TYPE, GKT_CLKRST_##name##_CLOCK_ID)

enum {
	GKT_CLKRST_MSCLK_MCLK = 0,
	GKT_CLKRST_MSCLK_SCLK,
	GKT_CLKRST_MSCLK_MAX
};
typedef struct _gkt_clkrst_msclk_desc {
	gkt_clkrst_common_desc_s	common;
	gkt_clkrst_cdiv_attr_s		attrs[GKT_CLKRST_MSCLK_MAX];
} gkt_clkrst_msclk_desc_s;
#define GKT_CLKRST_MSCLK_DESC(name)	\
	{	\
		.common	= GKT_CLKRST_COMMON_DESC(name, MSCLK, 0, -1),	\
		.attrs	= {	\
			GKT_CLKRST_CDIV_ATTR(name##_MCLK),	\
			GKT_CLKRST_CDIV_ATTR(name##_SCLK),	\
		},	\
	}

typedef struct _gkt_clkrst_msclk_attr {
	uint32_t	clock[GKT_CLKRST_MSCLK_MAX];
} gkt_clkrst_msclk_attr_s;


/* mask of Single Bit / Multiple Bits */
#define GKT_CLKRST_MASK_SB(name, field)	\
	gkt_bit(GKT_CLKRST_SHIFT(name, field))
#define GKT_CLKRST_MASK_MB(name, field)	\
	gkt_mask(GKT_CLKRST_BITS(name, field), GKT_CLKRST_SHIFT(name, field))

/*
 * @brief   : checked whether the ${field} bit of GKT_CLKRST_${name} is set or not
 * @example : GKT_CLKRST_CHECK_BIT(STATUS, PLL_LOCKED)
 */
#define GKT_CLKRST_CHECK_BIT(name, field)	\
	(gkt_read_reg32(GKT_CLKRST_ADDRESS(name)) \
		& GKT_CLKRST_MASK_SB(name, field))
/*
 * @brief	: set ${field} bit of GKT_CLKRST_${name}, other bits are unchanged
 * @example : GKT_CLKRST_SET_BIT(STATUS, PLL_LOCKED)
 */
#define GKT_CLKRST_SET_BIT(name, field)	\
	do {	\
		uint32_t __reg_addr = GKT_CLKRST_ADDRESS(name);	\
		uint32_t __reg_value = gkt_read_reg32(__reg_addr);	\
		__reg_value |= GKT_CLKRST_MASK_SB(name, field);	\
		gkt_write_reg32(__reg_addr, __reg_value);	\
	} while(0)
/*
 * @brief	: clear ${field} bit of GKT_CLKRST_${name}, other bits are unchanged
 * @example : GKT_CLKRST_CLEAR_BIT(STATUS, PLL_LOCKED)
 */
#define GKT_CLKRST_CLEAR_BIT(name, field)	\
	do {	\
		uint32_t __reg_addr = GKT_CLKRST_ADDRESS(name);	\
		uint32_t __reg_value = gkt_read_reg32(__reg_addr); \
		__reg_value &= ~ GKT_CLKRST_MASK_SB(name, field);	\
		gkt_write_reg32(__reg_addr, __reg_value);	\
	} while(0)


/*
 * @brief	: get value of field ${field} of GKT_CLKRST_${name}
 * @example : GKT_CLKRST_GET_VALUE(PLL, DIV)
 */
#define GKT_CLKRST_GET_VALUE(name, field)	\
	((gkt_read_reg32(GKT_CLKRST_ADDRESS(name)) \
		& GKT_CLKRST_MASK_MB(name, field)) \
			>> GKT_CLKRST_SHIFT(name, field))

/*
 * @brief	: set field ${field} of GKT_CLKRST_${name} with value ${value}
 * @example : GKT_CLKRST_SET_VALUE(PLL, DIV, 4)
 */
#define GKT_CLKRST_SET_VALUE(name, field, value)	\
	do {	\
		uint32_t __reg_addr = GKT_CLKRST_ADDRESS(name);	\
		uint32_t __reg_value = gkt_read_reg32(__reg_addr); \
		__reg_value &= ~ GKT_CLKRST_MASK_MB(name, field); \
		__reg_value |= (value) << GKT_CLKRST_SHIFT(name, field);	\
		gkt_write_reg32(__reg_addr, __reg_value);	\
	} while(0)

/*
 * @brief	: set field ${field} of GKT_CLKRST_${name} with value 
 *            GKT_CLKRST_${name}_${field}_${value}
 * @example : GKT_CLKRST_SET_ITEM(PLL, DIV, 4)
 */
#define GKT_CLKRST_SET_ITEM(name, field, item)	\
	do {	\
		uint32_t __value = GKT_CLKRST_ITEM(name, field, item);	\
		GKT_CLKRST_SET_VALUE(name, field, __value);	\
	} while(0)


/*
 * @brief	: get value of GKT_CLKRST_${name}
 * @example : GKT_CLKRST_GET(PLL)
 */
#define GKT_CLKRST_GET(name)	\
	gkt_read_reg32(GKT_CLKRST_ADDRESS(name))

/*
 * @brief	: set GKT_CLKRST_${name} with value ${value}
 * @example : GKT_CLKRST_SET(PLL, 4)
 */
#define GKT_CLKRST_SET(name, value)	\
	do {	\
		uint32_t __reg_addr = GKT_CLKRST_ADDRESS(name); \
		gkt_write_reg32(__reg_addr, value);	\
	} while(0)


/*
 * enable & disable clock
 */
void gkt_clkrst_enable_clock(const gkt_clkrst_desc_u *desc);
void gkt_clkrst_disable_clock(const gkt_clkrst_desc_u *desc);
int gkt_clkrst_clock_is_enabled(const gkt_clkrst_desc_u *desc);

/*
 * config clock (enable)
 */
int gkt_clkrst_config_clock(const gkt_clkrst_desc_u *desc);
int gkt_clkrst_config_clock_ext(const gkt_clkrst_desc_u *desc, 
				uint32_t clock);

/*
 * select debounce clock
 */
void gkt_clkrst_select_dbclk(const gkt_clkrst_desc_u *desc, 
				uint32_t dbclk_type);

/*
 * get clock value
 * the clock desc type should be fdiv or cdiv, not base;
 */
uint32_t gkt_clkrst_get_src_clock(const gkt_clkrst_desc_u *desc);
uint32_t gkt_clkrst_get_clock(const gkt_clkrst_desc_u *desc);

uint32_t gkt_clkrst_get_next_clock(const gkt_clkrst_desc_u *desc,
				uint32_t *clock_div);

/*
 * reset peripheral
 */
void gkt_clkrst_reset_peripheral(const gkt_clkrst_desc_u *desc, uint32_t time_us);

#ifdef __cplusplus
}
#endif

#endif	/* GKT_CLKRST_H */

